Skip to content

数据库系统

数据库分类

  • 关系型数据库:关系数据库,是,借助集合代数等数学概念和方法来处理数据库中的数据。现实世界中的各种实体以及实体之间的各种联系均用关系模型来表示。简单说,关系型数据库是由多张能互相联接的二维行列表格组成的数据库。
  • NoSQL:泛指。随着互联网的兴起,传统的关系数据库在应付超大规模和高并发的纯动态网站已经显得力不从心,暴露了很多难以克服的问题,而非关系型的数据库则由于其本身的特点得到了非常迅速的发展。NoSQL数据库的产生就是为了
  • 内存数据库:

缓存技术

  • MemCache:Memcache是一个高性能的分布式的内存对象缓存系统,用于动态Web应用以减轻数据库负载。Memcachej通过在内存里维护一个统一的巨大的hash表,它能够用来存储各种格式的数据,包括图像、规频、文件以及数据库检索的结果等。

  • Redis:Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

Redis.与Memcache的差异

  • 1、Redis:和Memcache 。他们都。同时,例如图片、现频等等,Redis:还支持Iist、set、hash等数据结构的存储。
  • 2、在Redis中,并不是所有的数据都一直存储在内存中的。这是和Memcache相比一个最大的区别。当物理内存用完时,Redis可以将一些很久没用到的value交换到磁盘。
  • 5、Redis, 而Memcache只是简单地K/V缓存。

并发控制

  • 丢失更新:事务1对数据A进行了修改并写回,事务2也对A进行了修改并写回,此时事务2写回的数据会覆盖事务1写回的数据,就丢失了事务1对A的更新。即对数据A的更新会被覆盖
  • 不可重复读:事务2读A,而后事务1对数据A进行了修改并写回,此时若事务2再读A,发现数据不对。即一个事务重复读A两次,会发现数据A有误。
  • 读脏数据:事务1对数据A进行了修改后,事务2读数据A,而后事务1回滚,数据A恢复了原来的值,那么事务2对数据A做的事是无效的,读到了脏数据。

image.png

不规范化带来的四大问题

设有一个关系模式R(SNAME,CNAME,TNAME,TADDRESS),其属性分别表示学生姓名、选修的课程名、任课教师姓名和任课教师地址。仔细分析一下,就会发现这个模式存在下列存储异常 的问题:

  • (1):数据被重复存储,如某门课程有100个学生选修,那么在R的关系中就要出现100个元组,这门课程的任课教师姓名和地址也随之重复出现100次。
  • (2):修改导致数据不一致,如由于上述冗余问题,当需要修改这个教师的地址时,就要修改100个元组中的地址值,否则就会出现地址值不一致的现象。
  • (3):插入时异常,如不知道听课学生名单,这个教师的任课情况和家庭地址就无法进入数据库。
  • (4):删除了不该删除的数据,如当只有一条记录时,要删除这个学生选课信息,会将课程名、教师名和教师地址都给删除了。

反规范化技术

反规范化技术:规范化设计后,数据库设计者希望

采用反规范化技术的益处:降低连接操作的需求、降低外码和索引的数目,还可能减少表的数目,能够提高查询效率。

可能带来的问题:数据的重复存储,浪费了磁盘空间;可能出现数据的完整性问题,为了保障数据的一致性,增加了数据维护的复杂性,会降低修改速度。

具体方法:

  • (1)增加冗余列:,通过增加数据冗余减少或避免查询时的连接操作。
  • (2)增加派生列:在表中增加可以由本表或其它表中数据计算生成的列,减少查询时的连接操作并避免计算或使用集合函数。
  • (3)重新组表:如果许多用户需要查看两个表连接出来的结果数据,则把这
  • (4)水平分割表:根据一列或多列数据的值,把,主要用于表数据规模很大、表中数据相对独立或数据需要存放到多个介质上时使用。
  • (5)垂直分割表:对表进行分割,,主键与其它列放到另一个表中,在查询时减少 I/O次数。

Redis 数据结构

1.

Redis,英文全称是Remote Dictionary Server(远程字典服务),是一个开源的使用ANSC语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

与MySQL数据库不同的是,Redis的数据是存在内存中的。它的读写速度非常快,每秒可以处理超过10万次读写操作。因此redisi被广泛应用于缓存,另外,Redis也经常用来做分布式锁。除此之外,Redis支持事务、持久化、LUA脚本、LRU驱动事件、多种集群方案。

Redis 基础类型

五种基本类型:

类型特点
String最基本字符串类型
Hashkey-value 结构(field-value 对),适合对象存储
List有序可重复列表
Set无序不可重复集合
ZSet有序集合(带排序),考试重点(如排行榜),改版后考过相关命令

三种特殊类型:

类型用途
GeoRedis 3.2+,存储地理位置信息
HyperLogLog基数统计(如网站 UV 统计)
Bitmaps位图,单比特映射元素状态(类似操作系统位示图)

Redis 为什么这么快

  • 3.1
  • 3.2 :B+树的数据结构。
  • 3.3 :Redis支持多种数据数据类型,每种基本类型,可能对多种数据结构。什么时候,使用什么样数据结构,使用什么样编码,是redisi设计者总结优化的结果。
  • 3.4
    • IO 多路复用:其实就是一种同步O模型,它实现了一个线程可以监视多个文件句柄;一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;而没有文件句柄就绪时,就会阻塞应用程序,交出Cpu。
    • 单线程模型:Redis是单线程模型的,而单线程避免了CPU不必要的上下文切换和竞争锁的消耗。也正因为是单线程,如果某个命令执行过长(如ngetall命令),会造成阻塞。Redis是面向快速执行场景的数据库。,所以要慎用如smembers:和Irange、hgetall等命令。
    • Redis6.0引入了多线程提速,它的执行命令操作内存的仍然是个单线程。
  • 3.5 :Redis.直接自己构建了 VM机制,不会像一般的系统会调用系统函数处理,会浪费一定的时间去移动和请求。

缓存问题分析

缓存穿透

查询不存在的数据时,缓存和数据库均无记录,导致每次请求直达数据库。

解决方案:

方案说明
参数校验API 入口过滤非法值
空值缓存对空结果也缓存,设合理过期时间(需维护一致性)
布隆过滤器2022 考点,概率型数据结构快速判断存在性,空间效率极高

先来看一个常见的缓存使用方式:读请求来了,先查下缓存,缓存有值命中,就直接返回;缓存没命中,就去查数据库,然后把数据库的值更新到缓存,再返回。

缓存穿透:指,由于缓存是不命中时需要从数据库查询,查不到数据则不写入缓存,这将导致这个,进而给数据库带来压力。

通俗点说,读请求访问时,缓存和数据库都没有某个值,这样就会导致每次对这个值的查询请求都会穿透到数据库,这就是缓存穿透。

缓存穿透一般都是这几种情况产生的:

  • ,比如大多数用户都没开守护,但是你的每个请求都去缓存,查询某个userid查询有没有守护。
  • ,比如缓存和数据库的数据都被误删除了。
  • ,比如黑客故意捏造大量非法请求,以读取不存在的业务数据。

如何避免缓存穿透呢?一般有三种方法。

  • 1如果是非法请求,我们在API入口,对参数进行校验,过滤非法值。
  • 2如果。但是如有有写请求进来的话,需要更新缓存哈,以保证缓存一致性,同时,最后给缓存设置适当的过期时间。(业务上比较常用,简单有效)
  • 3使用。即一个查询请求过来时,先通过布隆过滤器判断值是否存在,存在才继续往下查。

缓存雪崩

大规模缓存失效引发数据库崩溃。场景:大量缓存同时过期 / Redis 集群整体故障。

解决方案优点缺点
过期时间随机化实现简单不能应对 Redis 宕机
缓存预热热点数据命中率高需维护热点列表
多级缓存高性能、降低 Redis 依赖本地缓存一致性问题
Redis 高可用避免单点故障成本高、架构复杂
限流 & 降级保护数据库用户体验可能受影响
热点数据永不过期命中率极高数据可能过期不一致

生产环境:过期时间随机化 + Redis 集群高可用 + 多级缓存 + 限流降级组合使用。


缓存雪崩:指缓存中数据大批量到过期时间,而查询数据量巨大,请求都直接访问数据库,引起数据库压力过大甚至down机。

缓存雪崩一般是由于大量数据同时过期造成的,对于这个原因,可通过,即让过期时间相对离散一点。

Redis故障宕机也可能引起缓存雪崩。这就需要构造Redis高可用集群。

缓存击穿

单点热点 key 失效(vs 雪崩的大面积失效)。如秒杀商品、爆款新闻过期并发查询。

解决方案说明
互斥锁2024 考点,原子操作控制并发重建缓存
逻辑永不过期设较长 TTL,异步线程监控热点 key 临近过期自动续期
集群分流Redis 集群分散读流量
多级缓存本地缓存 + 分布式缓存多层结构

缓存击穿:指热点key在某个时间点过期的时候,而恰好在这个时间点对这个Key有大量的并发请求过来,从而大量的请求打到数据库。

缓存击穿看着有点像缓存雪崩,其实它两区别是,缓存雪崩是指甚至down机,。可以认为击穿是缓存雪崩的一个子集吧。

解决方案就有两种:

  • 1.使用互斥锁方案。缓存失效时,不是立即去加载db数据,而是先使用某些带成功返回的原子操作命令,如(Redis的setnx)去操作,成功的时候,再去加载db数据库数据和设置缓存。否则就去重试获取缓存。
  • 2“永不过期”,是指没有设置过期时间,

热key 问题

什么是热Key呢?

在Redis中,我们把访问频率高的key,称为热点key。

如果某一热点key的请求到服务器主机时,由于请求量特别大,可能会导致主机资源不足,甚至宕机,从而影响正常的服务。

而热点Key是怎么产生的呢?主要原因有两个:

  • ,如秒杀、热点新闻等读多写少的场景。
  • ,比如固定名称key,Hash落入同一台服务器,瞬间访问量极大,超过机器瓶颈,产生热点Key问题。

如何解决热key问题?

  • :增加分片副本,均衡读流量;
  • ,即JVM 本地缓存,减少Redis的读请求。

Redis 过期策略

策略机制优点缺点
定时过期每个 key 创建独立定时器,到期立即清除内存友好消耗大量 CPU
惰性过期仅在访问 key 时判断是否过期节省 CPU过期 key 可能堆积
定期过期每隔 ~100ms 随机抽取部分 key 检查平衡 CPU 和内存清理不够及时

生产环境默认:惰性过期 + 定期过期结合。


expires字典会保存所有设置了过期时间的key的过期时间数据,其中,key是指向键空间中的某个键的指针,vaue是该键的毫秒精度的UNX时间戳表示的过期时间。键空间是指该Redis:集群中保存的所有键。

Redis中同时使用了惰性过期和定期过期两种过期策略。

假设Redis当前存放30万个key,并且都设置了过期时间,如果你每隔100ms就去检查这全部的key,CPU负载会特别高,最后可能会挂掉。

因此,,每隔100ms就随机抽取一定数量的key来检查和删除的。

但是呢,最后可能会有。在你获取某个key的时候,redis会检查一下,这个key如果设置了过期时间并且已经过期了,此时就会删除。

但是,如果定期删除漏掉了很多过期的key,然后也没走惰性删除。就会有很多过期key积在内存内存,直接会导致内存爆的。或者有些时候,业务量大起来了,redis的key被大量使用,内存直接不够了。

这种情况下,Redis.用8种内存淘汰策略保护自己~


Redis 内存淘汰策略

Redis 8 种内存淘汰策略

策略范围算法核心机制
volatile-lru过期 keyLRU淘汰最近最少使用
allkeys-lru所有 keyLRU全局淘汰冷数据
volatile-lfu过期 keyLFU淘汰最不常用(Redis 4.0+)
allkeys-lfu所有 keyLFU全局频率淘汰
volatile-random过期 key随机随机淘汰
allkeys-random所有 key随机全局随机
volatile-ttl过期 keyTTL淘汰即将过期 key
noeviction全局无淘汰内存满拒写报错(默认

生产环境优先 allkeys-lruallkeys-lfu


Redis 常用应用场景

  • 缓存:我们一提到edis,自然而然就想到缓存,国内外中大型的网站都离不开缓存。合理的利用缓存,比如缓存热点数据,不仅可以提升网站的访问速度,还可以降低数据库DB的压力。并且,Redis相比于memcached,还提供了丰富的数据结构,并且
  • 7.2 排行榜:当今互联网应用,有各种各样的排行榜,如电商网站的月度销量排行榜、社交八PP的礼物排行榜、小程序的投票排行榜等等。

以下是针对ZSet的常用操作命令:

(1)ZADD key score member [score member...]

将一个或多个成员元素及其分数值加入到有序集合中。

(2)ZCARD key

返回有序集合中的成员数量。

(3)ZSCORE key member
返回有序集合中指定成员的分数。
(4)ZRANGE key start stop [WITHSCORES]

返回有序集合中指定索引范围内的成员,可选择返回成员的分数。
(5)ZRANGEBYSCORE key min max [WITHSCORES][LIMIT offset count]

返回有序集合中分数范围内的成员,可选择返回成员的分数,并可指定返回结果的偏移量和数量。
(6)ZREM key member [member ...

移除有序集合中的一个或多个成员。
(7)ZINCRBY key increment member

将有序集合中指定成员的分数增加增量increment。
(8)ZCOUNT key min max

计算有序集合中分数范围内的成员数量。
(9)ZREVRANGE key start stop [WITHSCORES]

返回有序集合中指定逆序索引范围内的成员,可选择返回成员的分数。

(10)ZREVRANGEBYSCORE key max min [WITHSCORES][LIMIT offset count]
返回有序集合中指定逆序分数范围内的成员,可选择返回成员的分数,并可指定返回结果的偏移量和数量。
  • 7.3 计数器应用:各大网站、APP应用经常需要计数器的功能,如。这些播放数、浏览数一般要求实时的,每一次播放和浏览都要做加1的操作,如果并发量很大对于传统关系型数据的性能是一种挑战。Redis天然支持计数功能而且计数的性能也非常好,可以说是计数器系统的重要选择。
  • 7.4共享Session:如果一个分布式Web服务将用户的Session信息保存在各自服务器,用户刷新一次可能就需要重新登录了,这样显然有问题。实际上,可以
  • 7.5分布式锁:几乎每个互联网公司中都使用了分布式部署,分布式服务下,就会遇到,如秒杀、下单减库存等场景。可以用Redis的setnx:来实现分布式的锁。
  • 76社交网络:,由于社交网站访问量通常比较大,而且传统的关系型数据不太适保存这种类型的数据,R©dis提供的数据结构可以相对比较容易地实现这些功能。
  • 7.7消息队列:是大型网站必用中间件,如ActiveMQ、RabbitMQ、Kafka等流行的消息队列中间件,主要用于业务解耦、流量削峰及异步处理实时性低的业务。R©ds提供了,能实现一个简单的消息队列系统。另外,这个不能和专业的消息中间件相比。
  • 7.8位操作:用于,去重登录次数统计,某用户是否在线状态等等。腾讯10亿用户,要几个毫秒内查询到某个用户是否在线,能怎么做?这里要用到位操作一使用setbit、.getbit、bitcounti命令。原理是:redis内构建一个足够长的数组,每个数组元素只能是0和1两个值,然后这个数组的下标idex用来表示用户id(必须是数字哈),那么很显然,这个几亿长的大数组就能通过下标和元素值(0和1)来构建一个记忆系统然

Redis持久化机制

RDB vs AOF

对比RDB(快照)AOF(追加日志)
原理定时生成内存快照 → dump.rdb记录每个写命令到 appendonly.aof
触发手动 save/bgsave,自动按时间 + 写次数always/everysec/no
优点文件小、恢复快、适合大规模备份数据安全性高、准实时持久化
缺点两次快照间可能丢数据文件大、恢复慢
类比全量备份操作日志

生产:RDB + AOF 混合持久化(Redis 4.0+)。


持久化机制优缺点

8.

Redis,是基于库,既然它是基于内存的,如果Redis.服务器挂了,数据就会丢失。为了避免数据丢失了,Redis?提供了持久化,即把数据保存到磁盘。Redis提供了RDB和AOF两种持久化机制。

  • RDB:就是上。什么是快照?可以这样理解,给当前时刻的数据,拍一张照片,然后保存下来。
    • RDB持久化,是指在指定的时间间隔内,执行指定次数的写操作,。执行完操作后,在,Redis。RDB触发机制主要有:手动触发(save同步、bgsave:异步)、自动触发。
    • RDB的优点:适合,如备份,全量复制等
    • RDB缺点:没办法做到。新老版本存在RDB格式兼容问题
  • AOF:采用,追加到文件中,重启时据。它主要解决数据持久化的实时性问题。默认是的。
    • AOF的优点:数据的一致性和完整性更高
    • AOF的缺点:AOF记录的内容越多,文件越大,数据恢复变慢。

常见问题


高可用架构

模式核心机制优势缺陷
主从模式Master 写 + Slave 读,读写分离,全量+增量复制,repl_backlog 断线续传架构简单主节点故障需人工切换
哨兵模式多 Sentinel 心跳检测(PING),主观/客观下线,自动 failover(Redis 2.8+)自动故障检测与切换部署复杂,全量副本
Cluster 集群16384 Hash Slot(CRC16+取模),Gossip 协议通信,302 重定向可水平扩容,分布式存储配置维护复杂

演进:主从 → 哨兵(自动故障转移)→ Cluster(分布式+在线扩容)。

  • 小规模:主从 + 哨兵
  • 中大规模:Redis Cluster

9.

我们在项目中使用Redis,肯定不会是单点部署Redis服务的。因为,单点部署一旦宕机,就不可用了。为了实现高可用,通常的做法是,,其中一台挂了也可以继续提供服务。Redis实现高可用有三种部署模式:

主从模式

9.1主从模式:Rdis部署了多台机器,有主节点,负责读写操作,有从节点,只负责读操作。从节点的数据来自主节点,实现原理就是主从复制机制。

主从复制包括两种。一般当,.或者认为是第一次连接,就采用全量复制。

slave与master3全量同步之后,

当master节点,接下来在Master-节点上调用的。执行此函数之前呢,master节点会判断用户执行的命令是否有数据更新,如果有数据更新的话,并且slave节点不为空,就会执行此函数。这个函数作用就是:

Redis的

  • SAVE:同步执行,直接在主进程调用rdbsave函数,阻塞所有客户端请求直至RDB文件生成完成。
  • BGSAVE:异步执行,,仅fok阶段有短暂阻塞
件,主要用于实现数据同步。

核心功能repl backlog通过环形数组记录主节点(master).执行的写命令及其offset,,确保从节点(slave) 在断线后能通过该日志恢复数据同步。当主节点写入新数据时,旧日志会被覆盖,但未同步的增量数据会保留在日志中,直到从节点完成同步。

  • 全量同步:
  • 增量同步:
Redis 主从复制流程

全量同步(首次):从库发 PSYNC ? -1 → 主库返回 FULLRESYNC replid offset → 主库 bgsave 生成 RDB → 发送 RDB → 从库清空本地加载 RDB → 主库发送 repl_backlog 中命令。

增量同步(断线重连):从库发 PSYNC replid offset → 主库判断 replid 一致 → 回复 CONTINUE → 从 repl_backlog 读取 offset 之后的命令发送。

哨兵模式

主从模式中,,需要人工将从节点晋升为主节点,同时还要通知应用方更新主节点地址。显然,多数业务场景都不能接受这种故障处理方式。Redis从2.8开始正式提供了 Redis Sentinel(哨兵)架构来解决这个问题。

  • 哨兵模式,由一个或多个Sentinels实例组成的Sentinel系统,它可以,并在被监视的主节点进入下线状态时,自动将下线主服务器属下的某个从节点升级为新的主节点。但是呢,一个哨兵进程对Redds节点进行监控,就可能会出现问题(单点问题),因此,可以使用,并且各个哨兵之间还会进行监控。

简单来说,哨兵模式就三个作用:

  • 哨兵,修改配置文件,让它们切换主机;
  • 哨兵之间还会

故障切换的过程是怎样的呢

  • 假设主服务器宕机,,仅仅是哨兵1主观的认为主服务器不可用,这个现象成为。当后面的哨兵也检测到主服务器不可用,并且数量达到一定值时,那么哨兵之间就会进行一次投票,投票的结果由一个哨兵发起,进行failover操作。,这个过程称为。这样对于客户端而言,一切都是透明的。

哨兵的工作模式如下:

  • 每个Sentinell以。如果一个实例(instance)距离最后一次有效回复PING命令的时间
  • 如果一个Masteri被标记为主观下线,则
  • (大于等于配置文件指定的值)在指定的时间范围内确认Master的确进入了主观下线状态,则Master会被标记为
  • 在一般情况下,每个Sentinel会以。当Master被Sentinel标记为客观下线时,Sentinel向下线的Master的所有Slave发送INFO命令的频率会从10秒一次改为每秒一次
  • Sentinell同意Masteri已经下线,Master的客观下线状态就会被移除;,Master的主观下线状态就会被移除。

Cluster集群模式

  • 哨兵模式基于主从模式,实现读写分离,它还可以自动切换,系统可用性更高。但是它并且不好在线扩容。因此,Cluster集群应运而生,它在Redis3.0加入的,实现了。对数据进行分片,也就是说,来解决在线扩容的问题。并且,它也提供复制和故障转移的功能。

  • Redis Cluster集群通过Gossip协议进行通信,。常用的Gossipi消息分为4种,分别是:ping、pong、meet、fail。

  • meet消息:。消息发送者通知接收者加入到当前集群,meet消息通信正常完成后,接收节点会加入到集群中并进行周期性的ping、pong消息交换。

  • ping消息:,集群内每个节点每秒向多个其他节点发送pig消息,用于

  • pong消息:当接收到ping、meet消息时。pong消息内部封装了自身状态数据。节点

  • fail消息:当节点,其他节点接收到fail消息之后把对应节点更新为下线状态。

特别的,每个节点是的。通讯时,使用特殊的端口号,即对外服务端口号加10000。例如如果某个node的端口号是6379,那么它与其它nodes通信的端口号是16379。nodes之间的通信采用特殊的二进制协议。

既然是分布式存储,Cluster集群使用的分布式算法是一致性Hash嘛?并不是,而是

插槽算法

image.png

image.png


分布式锁 TODO

方案核心缺陷
SETNX + 过期时间SETNX key value 加锁,DEL key 解锁单节点故障锁丢失;长事务过期失效
看门狗续期(Redisson)后台线程定期续期,默认每 10s仍依赖单节点
Redlock 算法多独立 Redis Master(通常 5 个),超半数加锁成功 + 耗时 < TTL实现复杂,网络延迟影响

演进:SETNX → 看门狗 → Redlock(多节点多数派原则)。


基于 ZSet 的分布式锁命令

背记:ZADD 抢锁,ZREM 放锁,ZREMRANGEBYSCORE 清过期

操作命令
获取锁ZADD lock_zset <score> <lock_id>
释放锁ZREM lock_zset <lock_id>
清理过期锁ZREMRANGEBYSCORE lock_zset -inf <current_timestamp>
检查锁存在ZSCORE lock_zset <lock_id>

MySQL 分布式锁缺点

  1. 性能瓶颈:每次锁操作需执行 SQL
  2. 单点故障:MySQL 宕机锁全部失效
  3. 死锁处理复杂:需额外死锁检测和超时机制
  4. 实现复杂度高:需手动编写 SQL + 事务
  5. 可扩展性差:难以应对大规模分布式系统
  6. 锁粒度控制困难:基于行/表,粒度较粗

双写一致性

Cache-Aside 模式

读流程:请求缓存 → 命中返回 → 未命中查 DB → 更新缓存 → 返回数据。

写流程:更新数据库 → 删除/失效缓存(使后续读取获取最新数据)。

双写一致性解决方案:

  1. 分布式锁(Redis SETNX):写前加锁,读写串行化
  2. 延迟双删:写库后删一次,延迟 500ms 再删一次
  3. 异步消息队列(Canal + Kafka):监听 binlog 异步失效缓存
  4. 版本号/逻辑时钟:缓存带版本,读时比对 DB 版本

Redis 事务

跳跃表

其他

练习题

练习题1

试题三数据库redis

某电商平台在去年双十二大促期间遭遇了严重的数据库性能问题:

订单处理延迟超过2秒,严重影响用户体验 Redis缓存使用率飙升至95%以上,接近容量极限 大量无效订单 ID请求导致缓存击穿,数据库直接承受巨大压力 数据库连接池耗尽,险些导致服务宕机

技术团队经过分析后,决定对系统架构进行优化: 采用Redis哨兵模式实现主从分离和高可用 数据更新策略采用 write-through 模式 引入布隆过滤器防止缓存击穿 对数据库进行读写分离和连接池优化 尽管如此,团队在方案评审时仍然存在一些技术争议,需要进一步明确设计细节。

问题1(8分):

(1)在当前电商场景下,相对于Memcached,Redis有哪两个主要优势?

(2)布隆过滤器是如何防止缓存击穿的?

问题2(10分):

在write-through和write-behind两种策略中,从数据一致性、性能、使用场景和实现复杂度四个维度进行比较,并说明在本案例中选择wite-through策略的优势。

问题3(7分):假设缓存已经击穿(如布隆过滤器误判或热点数据突然失效),大量请求直接涌向数据库。请提出两种防止数据库因此宕机的方案,并简要说明其原理和适用场景。


答案: 问题1:

(1)Redis在当前电商场景下的两个优势: 丰富的数据结构支持:Redis支持字符串、哈希、列表、集合、有序集合等多种数据结构,而Memcached仅支持简单的键值对。在电商场景中能支持对商品排行、展示商品信息等复杂需求。

数据持久化能力:Redis提供RDB快照和AOF日志两种持久化机制,而Memcached不支持持久化。能更好的保证可靠性。


(2)布隆过滤器可以用于检索一个元素是否在一个集合中。因此,在查询Reds之前,先查询布隆过滤器,判断请求的key是否可能存在。不存在,直接返回空结果,避免查询Redis和数据库。


问题2:

image.png

选择write-through策略的优势:

保证交易数据准确性:双十二期间,订单、支付、库存等核心数据必须准确无误,write-through确保缓存和数据库强一致,避免超卖、重复支付等问题

简化故障处理:同步写入模式下,写入失败可以立即感知和处理,而write-behind在异步写入失败时可能导致数据丢失且难以恢复

符合电商业务特性:电商核心业务对数据准确性要求高于写入性能,短暂的写入延迟(通常几十毫秒)对用户体验影响较小

问题3:

  • 方案1:数据库限流与熔断
    • 原理:在应用层或数据库代理层实施请求限流,当数据库压力超过阈值时:
    • 限流:限制单位时间内访问数据库的请求数量,超出的请求直接返回降级响应(如“系统繁忙,请稍后重试”)
    • 熔断:当数据库错误率或响应时间超过阈值,暂时切断对数据库的访问,直接返回缓存数据或默认值
    • 慢查询熔断:自动识别并拦截执行时间过长的SQL
    • 适用场景:突发流量冲击、缓存大规模失效、异常攻击等情况
  • 方案2:数据库资源弹性扩展与读写分离
    • 原理:通过架构优化分散数据库压力:
    • 读写分离:将读请求路由到只读副本,写请求到主库
    • 连接池优化:合理配置连接数,避免连接耗尽
    • 资源弹性扩展:在云环境下,根据监控指标自动扩展数据库资源
    • SQL优化与索引:对慢查询进行优化,建立合适的索引
    • 适用场景:常态化大促准备、数据库性能瓶颈等情况
  • 补充方案(可选):
    • 多级缓存架构:在Redis前增加本地缓存(如Guava Cache),减少Redis压力
    • 热点数据预加载与永不过期:对预测的热点商品数据提前加载到Reds并设置为永不过期(或异步更新)
    • 请求合并:将短时间内对同一数据的多次查询合并为一次数据库查询